package com.birdboot.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求对象
 * 该类的每一个实例用于表示浏览器发送过来的一个HTTP协议规定的请求内容
 * 每个请求由三个部分构成:
 * 请求行,消息头,消息正文
 */
public class HttpServletRequest {
    private Socket socket;
    //请求行相关信息

    private String method;//保存请求行中的请求方式
    private String uri;//抽象路径
    private String protocol;//协议版本
    /*
        uri:/regUser?username=范传奇&password=123456&nickname=传奇&age=22
     */
    private String requestURI;//保存uri中的请求部分("?"左侧内容)
    private String queryString;//保存uri中的参数部分("?"右侧内容)
    private Map<String,String> parameters = new HashMap<>();//保存每一组参数


    //消息头相关信息
    private Map<String,String> headers = new HashMap<>();

    public HttpServletRequest(Socket socket) throws IOException, EmptyRequestException {
        this.socket = socket;
        //1解析请求行
        parseRequestLine();
        //2解析消息头
        parseHeaders();
        //3解析消息正文
        parseContent();

    }
    //解析请求行
    private void parseRequestLine() throws IOException, EmptyRequestException {
        //读取浏览器发送过来的一行字符串(请求行)
        String line = readLine();

        if(line.isEmpty()){
            //没有请求行,断定为空请求
            throw new EmptyRequestException();
        }

        System.out.println("请求行:"+line);

        //将请求行的三个信息拆分出来并赋值上述三个变量
        //[GET, /index.html, HTTP/1.1]
        String[] data = line.split("\\s");
        method = data[0];
        uri = data[1];
        protocol = data[2];

        //进一步解析uri
        parseURI();

        System.out.println("method:"+method);//GET
        System.out.println("uri:"+uri);// /index.html
        System.out.println("protocol:"+protocol);//HTTP/1.1
    }

    //进一步解析uri
    private void parseURI(){
        /*
            String requestURI
            String queryString
            Map parameters

            uri有两种情况:
            1:不含有参数的
              例如: /index.html
              直接将uri的值赋值给requestURI即可.

            2:含有参数的
              例如:/regUser?username=fancq&password=123456&nickname=chuanqi&age=22
              将uri中"?"左侧的请求部分赋值给requestURI
              将uri中"?"右侧的参数部分赋值给queryString
              将参数部分首先按照"&"拆分出每一组参数，再将每一组参数按照"="拆分为参数名与参数值
              并将参数名作为key，参数值作为value存入到parameters中。
                requestURI:/regUser
                queryString:username=&password=123456&nickname=chuanqi&age=22
                parameters:
                    key             value
                  username          ""
                  password          123456
                  nickname          chuanqi
                  age               22
         */
        /*
            uri情况:
            /index.html     data:[/index.html]
            /regUser?username=xxx&password=xxx....  data:[/regUser, username=xxx&password=xxx....]
            /regUser?       data:[/regUser]
         */
         String[] data = uri.split("\\?");
         requestURI = data[0];
         if(data.length>1){//长度>1说明"?"右侧有参数部分
             queryString = data[1];
             parseParameters(queryString);
         }

        System.out.println("requestURI:"+requestURI);
        System.out.println("queryString:"+queryString);
        System.out.println("parameters:"+parameters);

    }

    private void parseParameters(String line){
        //先将参数转码
        try {
            line = URLDecoder.decode(line,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String[] paras = line.split("&");
        for(String para : paras){
            String[] arr = para.split("=",2);
            parameters.put(arr[0],arr[1]);
        }
    }

    //解析消息头
    private void parseHeaders() throws IOException {
        while(true){
            String line = readLine();
            if(line.isEmpty()){//如果readLine返回空字符串,说明单独读取了回车+换行
                break;
            }
            System.out.println("消息头:"+line);
            //将消息头按照"冒号空格"拆分出消息头名字和对应的值,以key,value存入headers
            String[] data = line.split(":\\s");
            headers.put(data[0],data[1]);
        }
        System.out.println("headers:"+headers);
    }
    //解析消息正文
    private void parseContent(){
        //判断本次是否为POST请求
        if("post".equalsIgnoreCase(method)){
            //获取消息头Content-Length的值来确定正文的字节数
            String value = headers.get("Content-Length");
            if(value!=null){//value不为空,说明本次请求包含消息头:Content-Length
                int contentLength = Integer.parseInt(value);
                System.out.println("正文长度是=============>"+contentLength);
                try {
                    InputStream in = socket.getInputStream();
                    byte[] data = new byte[contentLength];//根据正文长度创建等长数组
                    in.read(data);//将正文所有字节读取到数组中
                    //根据消息头Content-Type来解码正文数据
                    String contentType = headers.get("Content-Type");
                    if("application/x-www-form-urlencoded".equals(contentType)){
                        //form表单传递的数据,原GET请求在抽象路径中"?"右侧的字符串
                        String line = new String(data, StandardCharsets.ISO_8859_1);
                        System.out.println("正文内容==============>"+line);
                        parseParameters(line);
                    }
//                    else if("XXXX/XXXXXX".equals(contentType)){
//                      //其他类型的正文数据
//                    }


                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }
    }


    private String readLine() throws IOException {
        InputStream in = socket.getInputStream();
        int d;
        char pre='a',cur='a';//pre记录上次读取的字符,cur记录本次读取的字符
        StringBuilder builder = new StringBuilder();

        while((d = in.read())!=-1){
            cur = (char)d;//本次读取到的字符
            if(pre==13 && cur==10){//上次是否读取回车符,本次读取的是否为换行符
                //若满足条件,则认为一行读取完毕
                break;
            }
            builder.append(cur);
            pre = cur;//在下次读取字符前,将本次读取的字符记录为上次读取的字符
        }
        return builder.toString().trim();//获取最终读取到的一行字符串
    }

    public String getMethod() {
        return method;
    }

    public String getUri() {
        return uri;
    }

    public String getProtocol() {
        return protocol;
    }

    public String getHeader(String name) {
        return headers.get(name);
    }

    public String getRequestURI() {
        return requestURI;
    }

    public String getQueryString() {
        return queryString;
    }

    public String getParameter(String name) {
        return parameters.get(name);
    }
}




